/** \file   MINI.g
*   \brief  Proposed grammer written using ANTLR which parses the code and returns Code Graph
*   \author Mohamad Noureddine
*   \date   30-06-2011
*/
grammar MINI;

options 
{
    backtrack	= true;
    memoize     = true;
    k           = 3;
    language	= C;
}


@lexer::header
{
#define	ANTLR3_INLINE_INPUT_ASCII
}

@parser::includes
{
 #include <util.h>
 #include <iostream>
 #include <string>
 #include <map>
}

// Use @preincludes for things you want to appear in the output file
//     before #include <antlr3.h>
//     @includes to come after #include <antlr3.h>
//     @header for things that should follow on after all the includes.
@members 
{
    PDTCodeGraph * cg; //!< Code Graph to generate and return
    Name2Node_t name2node; //!< map between variable name and corresponding node in graph
    Val2Node_t val2node; //!< map between constant value and corresponding node in graph
    Name2Func_t name2func; //!< map between function name and function declaration construct
    UnresolvedFunc_t unresolvedfunc; //!< stack of unresolved function class
    string function_prefix; //!< string that holds the function prefix if any 
    string quantifier_prefix; //!< string that holds the quantifier prefix if any
    PDTVar * __funcReturn; //!< PDTVar to hold function return variable
    int unresolvedSize; //!< unresolved functions number
    Name2Assign_t name2assign; //!< unresolved assignment statements
    ScopeStack_t scopeStack; //!< stack of current scopes
    unsigned int quantifier_cnt; //!< count of quantifiers for scope resolution
}

translation_unit returns [PDTCodeGraph * _cg, Name2Node_t _name2node]
@init{
    cg = new PDTCodeGraph();
    function_prefix = "";
    quantifier_prefix = "";
    unresolvedSize = 0;
    quantifier_cnt = 0;
}
@after{/*cout<<"Parsing Complete"<<endl;*/
	if( unresolvedSize != 0 ) {
		fprintf( stderr, "There are still undefined functions:\n" );
        UnresolvedFunc_t::iterator _it = unresolvedfunc.begin();
        for(; _it != unresolvedfunc.end(); _it++ ) {
            cout << (*_it)->getName() << endl;
        }
		exit( EXIT_FAILURE );
	}
}
    :(a=code)+
        {
            $_cg = cg;
            $_name2node = name2node;
        }
    ;

/*
 program code
 */
code
    : a=declaration ';'
    | (statement)+
    | function_declaration
    ;


declaration returns [PDTVar * var_ptr]
    : e=declaration_specifier j=declarator  {
        // search for variable in map, if found return error
        // if not then create a new with the appropriate types 
        // TODO: add support for arrays
        string name = *($j.val);
        Name2Node_t::iterator it;
        it = name2node.find(name);
        if ( it == name2node.end() ) {
            // create new node for appropriate type
            switch ( $e.type ) {
                case CGINT: {
                    PDTNumVar * numvar_ptr = new PDTNumVar( *cg, name );
                    name2node[name] = numvar_ptr;
                    $var_ptr = numvar_ptr;
                    break;
                }
                case CGBOOL: {
                    PDTBoolVar * boolvar_ptr = new PDTBoolVar( *cg, name );
                    name2node[name] = boolvar_ptr;
                    $var_ptr = boolvar_ptr;
                    break;
                }
                case CGINTARRAY: { // add support
                    PDTSequence * sequence_ptr = new PDTSequence( *cg, name, &PDTType::Int, NULL, NULL, $e.size );
                    PDTSequenceVar * seqvar_ptr = new PDTSequenceVar ( *cg, *sequence_ptr, name );
                    name2node[name] = seqvar_ptr;
                    $var_ptr = seqvar_ptr;
                    break;
                }
                case CGBOOLARRAY: { // add support
                    PDTSequence * sequence_ptr = new PDTSequence( *cg, name, &PDTType::Bool, NULL, NULL, $e.size );
                    PDTSequenceVar * seqvar_ptr = new PDTSequenceVar ( *cg, *sequence_ptr, name );
                    name2node[name] = seqvar_ptr;
                    $var_ptr = seqvar_ptr;
                    break;
                }
                default:
                    break;
            }
        } else {
            // throw error of declaring an already declared variable
            fprintf( stderr, "Syntax Error! Variable \%s already defined \n", name.c_str() );
            exit(EXIT_FAILURE);
        }
    }
    ;

declaration_specifier returns [CGTypeEnum_t type, int size]
    :   e=type_specifier {
        $type = $e.type;
        $size = $e.size;
    }
    ;

/*
 declarator returns string pointer with variable name
 */
declarator returns [string *val]
    : e=direct_declarator {$val=$e.val;}
    ;

/*
 type specifier (int, bool, etc.)
 */
type_specifier returns [CGTypeEnum_t type, int size] // return type enum and array size if applicable
@init {
	$size = -1;
} 
    :
    'int' // integer type 
        {
            $type = CGINT; //  return integer type
        }
    | 'int' '[' (s=constant {$size = $s.value;})? ']' // array of integeres
        {
            $type = CGINTARRAY; // return integer array type 
        }
    | 'boolean' // boolean type
        {
            $type = CGBOOL; // return boolean type
        }
    | 'boolean' '[' (s=constant {$size = $s.value;})? ']' // array of booleans
        {
            $type = CGBOOLARRAY; // return boolean array type
        }
    | pa=pair_specifier // pair
    ;

pair_specifier
    : 'pair<' a=type_specifier ',' b=type_specifier '>'
    ;


/*
 direct declarator return the variable name
 */
direct_declarator returns [string *val]
    :   e=IDENTIFIER 
     {
      string name ((const char*)$e.text->chars);
      
      string * ret = new string;
      *ret = resolveName(name, scopeStack);
      $val = ret;
     }//declarator_suffix?
    ;

/*
 declarator_suffix deprecated for now
 */
declarator_suffix
    :   '[' constant_expression ']'
    ;

	
initializer returns [int vid]
    : e=conditional_expression
    ;


expression returns [PDTExp * exp_ptr, PDTStatement * stmt_ptr, PDTANTLRProp_t prop]
    : a=assignment_expression {
        $exp_ptr = $a.exp_ptr; 
        $stmt_ptr = $a.stmt_ptr;
        $prop = $a.prop;
      }
    ;

constant_expression returns [int vid]
    : a=conditional_expression 
    ;

assignment_expression returns [PDTExp * exp_ptr, PDTStatement * stmt_ptr, PDTANTLRProp_t prop]
    : a=lvalue assignment_operator b=assignment_expression {
        // Create an assign task and add it to the code graph
        // Need to check if exp_ptr is NULL in order to make
        // sure that no two assigns are done in the same statement
        if ($b.exp_ptr != NULL) {
            PDTAssign * assign = new PDTAssign ( *cg, *$a.var_ptr, *$b.exp_ptr );
            $exp_ptr = NULL; // return NULL since no need to return anything
            updateAntlrProps(*assign, $a.prop);
            $stmt_ptr = assign;
            $prop = $a.prop;
        } else {
            fprintf( stderr, "Syntax error! No two assignments are allowed in the same statement! \n" );
            exit( EXIT_FAILURE );
        }
    }
    | d=lvalue assignment_operator e=function_call {
        // Create an assign task and add it to the code graph
        // Need to check if exp_ptr is NULL in order to make
        // sure that no two assigns are done in the same statement
        if ($e.exp_ptr != NULL) {
        	if ( $e.unrslvd_ptr == &PDTUnresolvedExp::unresolvedExp ) {
        		PDTAssign * assign = new PDTAssign ( *cg, *$d.var_ptr, *$e.unrslvd_ptr );
        		$exp_ptr = NULL;
                updateAntlrProps(*assign, $d.prop);
        		$stmt_ptr = assign;
        		name2assign.insert( pair < string, PDTAssign * > ($e.funcName, assign) );
        	} else {
            	PDTAssign * assign = new PDTAssign ( *cg, *$d.var_ptr, *$e.exp_ptr );
            	$exp_ptr = NULL; // return NULL since no need to return anything
                updateAntlrProps(*assign, $d.prop);
            	$stmt_ptr = assign;
            }
        } else {
            fprintf( stderr, "Syntax error! No two assignments are allowed in the same statement! \n" );
            exit( EXIT_FAILURE );
        }
    }
    | c=conditional_expression {
        $exp_ptr = $c.exp_ptr; 
        $prop = $c.prop;
        $stmt_ptr = NULL;
       } 
    | s=sequence_assignment {
    	$stmt_ptr = $s.stmt_ptr;
        $prop = $s.prop;
    	$exp_ptr = NULL;
    }
    ;

sequence_assignment returns [PDTStatement * stmt_ptr, PDTANTLRProp_t prop]
@init {
	PDTSequenceVar * var_ptr;
	vector <PDTNode*> * nodeVec = new vector <PDTNode*>();
}
	: i=IDENTIFIER {
		string name ( (const char*)$i.text->chars );
		
		bool found = false;
		PDTNode * varNode = checkName(name, scopeStack, name2node, found);
        if ( found ) {
        	if ( varNode->isSequenceVar() ){
                $prop = createAntlrProp($i->line, $i->charPosition, $i->start, $i->stop);
                updateAntlrProps(*varNode, $prop);
            	var_ptr = dynamic_cast<PDTSequenceVar*>( varNode ); // cast PDTNode * to PDTVar *
            } else {
            	fprintf( stderr, "Cannot assign a sequence to a nonsequence type\n" );
            	exit( EXIT_FAILURE );
            }
        } else {
            fprintf( stderr, "Syntax Error! Variable \%s not found on line \%d\n", name.c_str(), $i.line);
            exit( EXIT_FAILURE );
        }
	} '=' ( ('{' cond=conditional_expression {
		PDTNode * node = dynamic_cast <PDTNode*>($cond.exp_ptr);
		nodeVec->push_back( node );
	} (',' c1=conditional_expression {
		PDTNode * node = dynamic_cast <PDTNode*>($c1.exp_ptr);
		nodeVec->push_back( node );
	})* '}' {
		PDTConstSequence * const_ptr = new PDTConstSequence( *cg, nodeVec );
		PDTSequenceAssign * assign_ptr = new PDTSequenceAssign( *cg, *var_ptr, *const_ptr );
        updateAntlrProps(*assign_ptr, $prop);
		$stmt_ptr = assign_ptr;
	}) | ss=STRING {
            string str ((const char *) $ss.text->chars);
            unsigned int i = 1; // start from 1 and end 1 before
                                // to accomodate for quotes
            for (; i < str.size()-1; i++) {
            PDTConst * constVal = PDTConst::makeConst(*cg, (long long) str[i]);
            PDTNode * val = constVal->getNode();
            nodeVec->push_back(val);
            }
		PDTConstSequence * const_ptr = new PDTConstSequence( *cg, nodeVec );
		PDTSequenceAssign * assign_ptr = new PDTSequenceAssign( *cg, *var_ptr, *const_ptr );
		$stmt_ptr = assign_ptr;

    })
	;
	
lvalue returns [PDTVar * var_ptr, PDTANTLRProp_t prop]
@init{
    PDTNode * varNode;
}
    : i=IDENTIFIER {
        // Get identifier name and check if it is valid
        // if valid then return the PDTVar back to the 
        // assignment expression.
        // TODO: Add support for [] access operator
        string name ( (const char*)$i.text->chars );
        
        bool found = false;
		varNode = checkName(name, scopeStack, name2node, found);

        if ( found ) {
            $var_ptr = dynamic_cast<PDTVar*>( varNode ); // cast PDTNode * to PDTVar *
            $prop = createAntlrProp($i->line, $i->charPosition, $i->start, $i->stop);
            updateAntlrProps(*varNode, $prop);
            
        } else {
            fprintf( stderr, "Syntax Error! Variable \%s not found on line \%d. \n", name.c_str(),$i.line );
            exit( EXIT_FAILURE );
        }
    }
        ('.' ('first' | 'second'))* ( '[' e=expression ']' {
            if ( varNode->isSequenceVar() ) {
            	PDTSequenceVar * seqVar = ((PDTSequenceVar*)varNode);
            	PDTSequence * seq = seqVar->getSequence();
            	if ( seq->isElementInt() ) {
                	PDTExpNum * num_ptr = dynamic_cast<PDTExpNum*>($e.exp_ptr);
                	PDTSequenceAccessNum * access_ptr = new PDTSequenceAccessNum ( *cg, *seqVar, *num_ptr );
                    updateAntlrProps(*access_ptr, $prop);
                	$var_ptr = access_ptr;
                } else if ( seq->isElementBoolean() ) {
                	PDTExpBool * num_ptr = dynamic_cast<PDTExpBool*>($e.exp_ptr);
                	PDTSequenceAccessBool * access_ptr = new PDTSequenceAccessBool( *cg, *seqVar, *num_ptr );
                    updateAntlrProps(*access_ptr, $prop);
               		$var_ptr = access_ptr;
                }
            } else {
                fprintf( stderr, "Syntax Error! Variable \%s is not an array \n", ((PDTVar*)varNode)->getName().c_str() );
                exit( EXIT_FAILURE );
            }
        })? 
    ;   

assignment_operator
    : '='
    ; 

conditional_expression returns [PDTExp * exp_ptr, PDTANTLRProp_t prop] 
    : a=logical_or_expression {
        $exp_ptr = $a.exp_ptr;
        $prop = $a.prop;
    } ( '->' b=logical_or_expression {
        // create a new PDTImplies structure and add to the code graph
        // note that we need to create it explicitly and not directly assign
        // it because of the * operator 
        PDTExpBool*boolExpA = dynamic_cast<PDTExpBool*>($a.exp_ptr);
        PDTExpBool*boolExpB = dynamic_cast<PDTExpBool*>($b.exp_ptr);
        PDTImplies * imply = new PDTImplies( *cg, *boolExpA, *boolExpB);
        $exp_ptr = imply;
    })*
    | '!' cexp=conditional_expression {
        PDTExpBool* boolExp = dynamic_cast<PDTExpBool*>($cexp.exp_ptr);
        PDTNot * notExp = new PDTNot(*cg, *boolExp);
        $exp_ptr = notExp;
    }
    ;
logical_or_expression returns [PDTExp * exp_ptr, PDTANTLRProp_t prop]
    : a=logical_and_expression {
        $exp_ptr = $a.exp_ptr;
        $prop = $a.prop;
    } ('||' b=logical_and_expression {
        PDTOr * _or = new PDTOr( *cg, *((PDTExpBool*)($a.exp_ptr)), *((PDTExpBool*)($b.exp_ptr)) );
        updateAntlrProps(*_or, $prop);
        $exp_ptr = _or;
    })*
    ;

logical_and_expression returns [PDTExp * exp_ptr, PDTANTLRProp_t prop]
    : a=exclusive_or_expression {
        $exp_ptr = $a.exp_ptr;
        $prop = $a.prop;
    } ('&&' b=exclusive_or_expression {
        PDTAnd * _and = new PDTAnd( *cg, *((PDTExpBool*)($a.exp_ptr)), *((PDTExpBool*)($b.exp_ptr)) );
        updateAntlrProps(*_and, $prop);
        $exp_ptr = _and;
    })*
    ;

exclusive_or_expression returns [PDTExp * exp_ptr, PDTANTLRProp_t prop]
    : a=equality_expression {
        $exp_ptr = $a.exp_ptr;
        $prop = $a.prop;
    } ('#' b=equality_expression {
        PDTXor * _xor = new PDTXor( *cg, *((PDTExpBool *)($a.exp_ptr)), *((PDTExpBool*)($b.exp_ptr)) );
        updateAntlrProps(*_xor, $prop);
        $exp_ptr = _xor;
    })*
    | q=quantifier_expression {
        $exp_ptr = $q.exp_ptr;
        $prop = $q.prop;
    }
    ;
    
/*
 quantified expressions: forall, thereexists
 */
quantifier_expression returns [PDTExp * exp_ptr, PDTANTLRProp_t prop]
@init {
	vector < PDTVar * > vars; // vector to hold variables in quantifier list
	quantifier_cnt++;
}
	: 'forall' {
		stringstream str;
		str << "Q" << quantifier_cnt;
		string qName;
		qName = str.str();
		scopeStack.push_front(qName);
	} {quantifier_prefix = "";} '(' q=quantifier_list { 
        vars.push_back($q.var_ptr); 
        $prop = $q.prop;
    }  (',' qm=quantifier_list { vars.push_back($qm.var_ptr); })* ')'
	xxx='{' c=conditional_expression '}' {
        unsigned int line = $xxx.line;
		if ( $c.exp_ptr->isBool() ) { // boolean expression
            PDTExpBool * exp = dynamic_cast<PDTExpBool*>($c.exp_ptr);
			PDTForall * forall_ptr = new PDTForall( *cg, vars, *exp);
            updateAntlrProps(*forall_ptr, $prop);
			$exp_ptr = forall_ptr;
			int s = vars.size();
			PDTVar * var;
			for ( int i = 0; i < s; i++ ) {
				var = vars[i];
				var->setQuantifier( forall_ptr );
			}
		} else { // not a boolean expression
			fprintf( stderr, "Quantified expression must be a boolean expression!\n" );
            cout << "line number: " << line << endl;
			exit( EXIT_FAILURE );
		}
		scopeStack.pop_front();
	}
	| 'exists' {
		stringstream str;
		str << "Q" << quantifier_cnt;
		string qName;
		qName = str.str();
		scopeStack.push_front(qName);
	} {quantifier_prefix = "";} '(' tq=quantifier_list { 
        vars.push_back($tq.var_ptr); 
        $prop = $tq.prop;
    } (',' tqm=quantifier_list { vars.push_back($tqm.var_ptr); })* ')' 
	xxx='{' d=conditional_expression '}' {
        unsigned int line = $xxx.line;
		if ( $d.exp_ptr->isBool() ) { // boolean expression
            PDTExpBool * exp = dynamic_cast<PDTExpBool*>($d.exp_ptr);
			PDTExist * exist_ptr = new PDTExist( *cg, vars, *exp);
            updateAntlrProps(*exist_ptr, $prop);
			$exp_ptr = exist_ptr;
			int s = vars.size();
			PDTVar * var;
			for ( int i = 0; i < s; i++ ) {
				var = vars[i];
				var->setQuantifier( exist_ptr );
			}
		} else { // not a boolean expression
			fprintf( stderr, "Quantified expression must be a boolean expression!\n" );
            cout << "line number: " << line << endl;
			exit( EXIT_FAILURE );
		}
		scopeStack.pop_front();
	}
	;
	
/* 
 quantifier list of identifiers
 */
quantifier_list returns [PDTVar * var_ptr, PDTANTLRProp_t prop]
@init {
	bool isRange = false;
}
	: t=type_specifier i=IDENTIFIER (':' l=inclusion_list { isRange = true;})? {
		string name ((const char*)$i.text->chars);
		string fname = resolveName(name, scopeStack);
        PDTIntRange * range_ptr = NULL;
        PDTIntRange * elrange_ptr = NULL;
        if (isRange){
           range_ptr = $l.range_ptr;
           elrange_ptr = $l.elrange_ptr;
        }
		Name2Node_t::iterator it;
        it = name2node.find(fname);
        if ( it != name2node.end() ) { // found
            fprintf( stderr, "Syntax Error! Variable \%s already defined in quantifier\n", name.c_str() );
            exit( EXIT_FAILURE );
        } else { // not found
           switch ( $t.type ) {
                case CGINT: {
                	PDTNumVar * numvar = new PDTNumVar( *cg, fname, NULL, range_ptr);
                    $var_ptr= numvar;
                    $prop = createAntlrProp($i->line, $i->charPosition, $i->start, $i->stop);
                    updateAntlrProps(*($var_ptr->getNode()), $prop);
                    name2node[fname] = numvar;
                    break;
                }
                case CGBOOL: {
                    PDTBoolVar * boolvar = new PDTBoolVar( *cg, fname );
                    $var_ptr= boolvar;
                    $prop = createAntlrProp($i->line, $i->charPosition, $i->start, $i->stop);
                    updateAntlrProps(*($var_ptr->getNode()), $prop);
                    name2node[fname] = boolvar;
                    break;
                }
                case CGINTARRAY: {
                	PDTSequence * sequence_ptr = new PDTSequence( *cg, fname, &PDTType::Int, range_ptr, elrange_ptr );
                    PDTSequenceVar * seqvar_ptr = new PDTSequenceVar ( *cg, *sequence_ptr, fname );
                    name2node[fname] = seqvar_ptr;
                    $prop = createAntlrProp($i->line, $i->charPosition, $i->start, $i->stop);
                    $var_ptr = seqvar_ptr;
                    updateAntlrProps(*($var_ptr->getNode()), $prop);
                	break;
                }
                case CGBOOLARRAY: {
                	PDTSequence * sequence_ptr = new PDTSequence( *cg, fname, &PDTType::Bool, range_ptr, elrange_ptr );
                    PDTSequenceVar * seqvar_ptr = new PDTSequenceVar ( *cg, *sequence_ptr, fname );
                    name2node[fname] = seqvar_ptr;
                    $prop = createAntlrProp($i->line, $i->charPosition, $i->start, $i->stop);
                    $var_ptr = seqvar_ptr;
                    updateAntlrProps(*($var_ptr->getNode()), $prop);
                }
                default: {
                    $var_ptr= NULL;
                    break;
                }
            }
        }
	}
	;

/*
 inclusion_list declares where the identifier is included
 */
inclusion_list returns [PDTIntRange * range_ptr, PDTIntRange * elrange_ptr]
@init {
	PDTExp * start_ptr;
	PDTExp * end_ptr;
}
	: 'int' {
		$range_ptr = NULL;
	}
	| 'boolean' {
		$range_ptr = NULL;
	}
	| '[' e1=additive_expression {
		start_ptr = $e1.exp_ptr;
	} '..' e2=additive_expression {
            end_ptr = $e2.exp_ptr;
	} ']' {
		PDTIntRange * range = new PDTIntRange( *cg, " ", start_ptr, end_ptr );
		$range_ptr = range;
	} ( '[' e3=additive_expression {
		start_ptr = $e3.exp_ptr;
	} '..' e4=additive_expression {
            end_ptr = $e4.exp_ptr;
	} ']' {
		PDTIntRange *elrange = new PDTIntRange( *cg, " ", start_ptr, end_ptr );
		$elrange_ptr = elrange;
	})?
	;

equality_expression returns [PDTExp * exp_ptr, PDTANTLRProp_t prop]
@init{
    bool equal = true;
}
    : a=relational_expression {
        $exp_ptr = $a.exp_ptr;
        $prop = $a.prop;
    }
        (('== '{equal = true;}|'!='{equal = false;}) b=relational_expression {
            if ( equal ) {
                PDTEqualBool * eq = new PDTEqualBool( *cg, *((PDTExpBool *)($a.exp_ptr)), *((PDTExpBool*)($b.exp_ptr)) );
                updateAntlrProps(*eq, $prop);
                $exp_ptr = eq;
            } else {
                PDTNotEqualBool * neq = new PDTNotEqualBool( *cg, *((PDTExpBool *)($a.exp_ptr)), *((PDTExpBool*)($b.exp_ptr)) );
                updateAntlrProps(*neq, $prop);
                $exp_ptr = neq;
            }
        })*
    ;

relational_expression returns [PDTExp * exp_ptr, PDTANTLRProp_t prop]
@init {
    int op = -1; //!< integer to hold current operation
}
    : a=additive_expression {
        $exp_ptr = $a.exp_ptr;
        $prop = $a.prop;
    }
        ( ('<' {op = 0;} |'>' {op = 1;} |'<=' {op = 2;}|'>=' {op = 3;} |'== ' {op = 4;} | '!=' {op = 5;}) b=additive_expression {
            // Use the op integer to indicate which operation to perform 
            // 0 for <, 1 for >, 2 for <= and 3 for >=
            if ( op == 0 ) { // <
                PDTLessThan * lt = new PDTLessThan( *cg, *((PDTExpNum *)($a.exp_ptr)), *((PDTExpNum*)($b.exp_ptr)) );
                $exp_ptr = lt;
            } else if ( op == 1 ) { // > 
                PDTGreaterThan * gt = new PDTGreaterThan( *cg, *((PDTExpNum *)($a.exp_ptr)), *((PDTExpNum*)($b.exp_ptr)) );
                $exp_ptr = gt;
            } else if ( op == 2 ) { // <=
                PDTLessThanEqual * lte = new PDTLessThanEqual( *cg, *((PDTExpNum *)($a.exp_ptr)), *((PDTExpNum*)($b.exp_ptr)) );
                $exp_ptr = lte;
            } else if ( op == 3 ) { // >=
                PDTGreaterThanEqual * gte = new PDTGreaterThanEqual( *cg, *((PDTExpNum *)($a.exp_ptr)), *((PDTExpNum*)($b.exp_ptr)) );
                $exp_ptr = gte;
            }
             else if ( op == 4 ) { // ==
                PDTEqualNum * eq = new PDTEqualNum( *cg, *((PDTExpNum *)($a.exp_ptr)), *((PDTExpNum*)($b.exp_ptr)) );
                $exp_ptr = eq;
            }
             else if ( op == 5 ) { // !=
                PDTNotEqualNum * ne = new PDTNotEqualNum( *cg, *((PDTExpNum *)($a.exp_ptr)), *((PDTExpNum*)($b.exp_ptr)) );
                $exp_ptr = ne;
            }
            updateAntlrProps(*$exp_ptr, $prop);
        })*
    | c=bool_constant { 
        PDTConst * cVal = PDTConst::makeConst(*cg,(bool)$c.value);
        PDTNode * node = cVal->getNode();
        $exp_ptr = dynamic_cast<PDTExp*>(node);
    }
;

// E x p r e s s i o n s

/*
 additive_expression handles +,-
 */
additive_expression returns [PDTExp * exp_ptr, PDTANTLRProp_t prop]
    : (a = multiplicative_expression {
        $exp_ptr = $a.exp_ptr;
        $prop = $a.prop;
    })      
    ('+' b = multiplicative_expression {
        // create a new addition PDTAdd structure and assign operands
        // note that a cast to PDTExpNum is needed and pointers are dereferenced
        // same applies for other operations on integers
        PDTAdd * add = new PDTAdd( *cg, *((PDTExpNum *)($a.exp_ptr)), *((PDTExpNum*)($b.exp_ptr)) );
        $exp_ptr = add;
        updateAntlrProps(*$exp_ptr, $prop);
    }
	| '-' c = multiplicative_expression {
        PDTSubt * sub = new PDTSubt( *cg, *((PDTExpNum *)($a.exp_ptr)), *((PDTExpNum*)($c.exp_ptr)) );
        $exp_ptr = sub;
        updateAntlrProps(*$exp_ptr, $prop);
    }
	)*
    ;

multiplicative_expression returns [PDTExp * exp_ptr, PDTANTLRProp_t prop]
    : (a = cast_expression {
        $exp_ptr = $a.exp_ptr;
        $prop = $a.prop;
    } )
	('*' b = cast_expression {
        PDTMult * mult = new PDTMult( *cg, *((PDTExpNum *)($a.exp_ptr)), *((PDTExpNum*)($b.exp_ptr)) );
        $exp_ptr = mult;
        updateAntlrProps(*$exp_ptr, $prop);
    }
	| '/' c = cast_expression {
        PDTDiv * div = new PDTDiv( *cg, *((PDTExpNum *)($a.exp_ptr)), *((PDTExpNum*)($c.exp_ptr)) );
        $exp_ptr = div;
        updateAntlrProps(*$exp_ptr, $prop);
    }
	| '%' d = cast_expression {
        PDTMod * mod = new PDTMod( *cg, *((PDTExpNum *)($a.exp_ptr)), *((PDTExpNum*)($d.exp_ptr)) );
        $exp_ptr = mod;
        updateAntlrProps(*$exp_ptr, $prop);
    })*
    ;

cast_expression returns [PDTExp * exp_ptr, PDTANTLRProp_t prop]
@init {
	bool oper = false;
	PDTUnaryOp * op;
	
}
    : (i = unary_operator {
    	oper = true;
    	op = $i.op;
    } )? e = postfix_expression {
    	if ( !oper ) {
    		$exp_ptr = $e.exp_ptr;
            $prop = $e.prop;
        }
    	else {
    		if ( $e.exp_ptr->isExpNum() ) {
    			PDTExpNum * expnum = (PDTExpNum*)($e.exp_ptr);
                $prop = $e.prop;
    			PDTUnaryNumExpr * unary_ptr = new PDTUnaryNumExpr( *cg, *op, *expnum );
    			$exp_ptr = unary_ptr;
                updateAntlrProps(*$exp_ptr, $prop);
    		} else {
    			fprintf( stderr, "Cannot use unary operator with non numerical type\n" );
    			exit ( EXIT_FAILURE );
    		}
    	}
    }
    ;

postfix_expression returns [PDTExp * exp_ptr, PDTANTLRProp_t prop]
    : i=primary_expression  {
        // TODO: add support for arrays
        $exp_ptr = $i.exp_ptr;
        $prop = $i.props;
    }
        ('.' ('first' | 'second')
        )*
        ( '[' abc=expression ']'
	)?
    ;   


unary_operator returns [PDTUnaryOp * op]
    :
    '+' {
    	$op = &(PDTUnaryOp::PDTUnaryPlus);
    }
    | '-' {
    	$op = &(PDTUnaryOp::PDTUnaryMinus);
    }
//    | '!'
    ;

primary_expression returns [PDTExp * exp_ptr, PDTANTLRProp_t props]
@init {
	PDTNode * varNode;
	string _name;
}
    :i = IDENTIFIER {
        // Get the name of the identifier and then 
        // check if found or not, if found return 
        // from map else return error
        string name ((const char*)$i.text->chars);
		
		bool found = false;
		varNode = checkName(name, scopeStack, name2node, found);

        if ( found ) { // found
            $exp_ptr = dynamic_cast<PDTExp*>(varNode); // return PDTVar * from name2node map
            _name = (dynamic_cast<PDTVar*>(varNode))->getName();
            $props = createAntlrProp($i->line, $i->charPosition, $i->start, $i->stop);
        } else { // not found
           fprintf( stderr, "Syntax Error! Variable \%s not found on line \%d. \n", name.c_str(), $i.line );
        }
    }
    ( '[' e=expression ']' {
            if ( varNode->isSequenceVar() ) {
            	PDTSequenceVar * seqVar = ((PDTSequenceVar*)varNode);
            	PDTSequence * seq = seqVar->getSequence();
            	if ( seq->isElementInt() ) {
                	PDTExpNum * num_ptr = dynamic_cast<PDTExpNum*>($e.exp_ptr);
                	PDTSequenceAccessNum * access_ptr = new PDTSequenceAccessNum ( *cg, *seqVar, *num_ptr );
                	$exp_ptr = access_ptr;
                } else if ( seq->isElementBoolean() ) {
                	PDTExpNum * num_ptr = dynamic_cast<PDTExpNum*>($e.exp_ptr);
                	PDTSequenceAccessBool * access_ptr = new PDTSequenceAccessBool( *cg, *seqVar, *num_ptr );
               		$exp_ptr = access_ptr;
                }
                updateAntlrProps(*$exp_ptr, $props);
            } else {
                fprintf( stderr, "Syntax Error! Variable \%s is not an array \n", _name.c_str() );
                exit( EXIT_FAILURE );
            }
        })? 
    | c = constant {
        // search for the constant value in the map, if found then return the 
        // appropriate expression, if not found then create a new PDTNumConstInt
        // and return it back and update the new map.
        Val2Node_t::iterator it;
        it = val2node.find( $c.value );
        if ( it == val2node.end() ) { // not found
            PDTNumConstInt * const_ptr = dynamic_cast <PDTNumConstInt*>(PDTConst::makeConst(*cg, $c.value));
            updateAntlrProps(*const_ptr, $c.props);
            $exp_ptr = const_ptr;
            val2node[$c.value] = const_ptr;
        } else { // found
            $exp_ptr = dynamic_cast < PDTExp * > ( (*it).second );
        }
        $props = $c.props;
    } 
    | '(' e=expression {
        $exp_ptr = $e.exp_ptr;
        $props = $e.prop;
    } ')'
    | f=function_call {
        $exp_ptr = $f.exp_ptr;
        $props = $f.props;
    }
    ;
    
/*
 function call on right side of assignment
 */
function_call returns [PDTExp * exp_ptr, string funcName, PDTExp * unrslvd_ptr, PDTANTLRProp_t props]
@init {
	bool isArg = false;
}
    : i=IDENTIFIER '(' (a=argument_list {isArg = true;})? ')' {
        // find the functions name and check if it is declared
        // if it is then point add it to the constructor of the 
        // function call, else leave for later to check 
        string name ((const char*)$i.text->chars);
        
        PDTFunctionCall * funcCall_ptr;
		if (isArg) {
        	funcCall_ptr = new PDTFunctionCall( *cg, *($a.argVec), name, NULL );
        } else {
        	funcCall_ptr = new PDTFunctionCall( *cg, name, NULL );
        }
        $funcName = name;

        Name2Func_t::iterator it;
        it = name2func.find(name);
        if ( it == name2func.end() ) { // not found
            if (!PDTFunctionCall::isSpecial(name)){ // TODO: this should be done more gracefully by returning a dummy function declaration instance that does the right thing.
                unresolvedfunc.push_back( funcCall_ptr );
                unresolvedSize++;
                $unrslvd_ptr = &PDTUnresolvedExp::unresolvedExp;
            }
        } else { // found
            funcCall_ptr->setFunctionDec( (*it).second, *cg );
//            $exp_ptr = dynamic_cast<PDTExp*> ( (*it).second->getReturnVar()->getNode() );
            string msg;
            if ( !funcCall_ptr->checkConsistency(msg) ) {
            	fprintf( stderr, "function call \%s: \%s on line \%d \n", name.c_str(), msg.c_str(), $i.line );
            	exit( EXIT_FAILURE );
            }
        }
        $props = createAntlrProp($i->line, $i->charPosition, $i->start, $i->stop);
        updateAntlrProps(*funcCall_ptr, $props);
        $exp_ptr = funcCall_ptr;
    } 
    ;

constant returns [ int value, PDTANTLRProp_t props ]
    : e=DECIMAL_LITERAL 
        {
             $value = atoi((const char*)$e.text->chars);
             $props = createAntlrProp($e->line, $e->charPosition, $e->start, $e->stop);
        }
    ;

bool_constant returns [bool value ]
    : 'true' {$value = true;} | 'false' {$value = false;}
    ;



// S t a t e m e n t s

/*
 statement in the code 
 */
statement returns [PDTStatement * stmt_ptr]
    : a=compound_statement {$stmt_ptr = $a.stmt_ptr;} 
    | b=expression_statement {$stmt_ptr = $b.stmt_ptr;}
    | c=selection_statement  {$stmt_ptr = $c.stmt_ptr;}
    | d=iteration_statement  {$stmt_ptr = $d.stmt_ptr;}
    | e=assertion_statement {$stmt_ptr = $e.stmt_ptr;}
    //| f=function_call_statement {$stmt_ptr = $f.stmt_ptr;assert($stmt_ptr!=NULL);}
    | g=pre_condition {$stmt_ptr = $g.stmt_ptr;}
    | h=post_condition {$stmt_ptr = $h.stmt_ptr;}
    | i=behavior_declaration {$stmt_ptr = $i.stmt_ptr;}
    ;

/*
 behavior of input output pairs
 */
behavior_declaration returns [PDTStatement * stmt_ptr]
@init {
	string name;
	vector <PDTBehavior *> goodVec;
	vector <PDTBehavior *> badVec;
	bool isGood = true;
}
	: '@behavior' i=IDENTIFIER {
        string _name ((const char*)$i.text->chars);
        name = _name;
	} '{'
		(('good' {isGood = true;} | 'bad' {isGood = false;}) 
		'{' 'input' '=' i1=input_output_assignment ';' 'output' '=' i2=input_output_assignment '}' {
			if ( isGood ) {
				PDTBehavior * good_ptr = new PDTBehavior( *cg, *($i1.retVec), *($i2.retVec) );
				goodVec.push_back( good_ptr );
			} else {
				PDTBehavior * bad_ptr = new PDTBehavior( *cg, *($i1.retVec), *($i2.retVec), false );
				badVec.push_back( bad_ptr);
			}
		}';')+
	'}' {
		PDTBehaviors * behavior_ptr = new PDTBehaviors( *cg, name, goodVec, badVec );
		$stmt_ptr = behavior_ptr;
	}
	;
	
/*
 assignment expressions for input output on behaviors
 */
input_output_assignment returns [vector < PDTStatement * > * retVec]
@init {
	$retVec = new vector<PDTStatement*>();
}
	: '{'
		a=assignment_expression {
			PDTStatement * stmt = $a.stmt_ptr;
			if ( stmt != NULL ) {
				if ( stmt->isAssign() ) {
					$retVec->push_back( stmt );
				} else {
					fprintf( stderr, "Cannot have a non assignment statement in behavior \n" );
					exit ( EXIT_FAILURE );
				}
			} else {
				fprintf( stderr, "Cannot have a non assignment statement in behavior \n" );
				exit ( EXIT_FAILURE );
			}
		} (',' b=assignment_expression {
			PDTStatement * stmt = $b.stmt_ptr;
			if ( stmt != NULL ) {
				if ( stmt->isAssign() ) {
					$retVec->push_back( stmt );
				} else {
					fprintf( stderr, "Cannot have a non assignment statement in behavior \n" );
					exit ( EXIT_FAILURE );
				}
			} else {
				fprintf( stderr, "Cannot have a non assignment statement in behavior \n" );
				exit ( EXIT_FAILURE );
			}
		})*
	'}'
	;

/*
 pre condition statement
*/
pre_condition returns [PDTStatement * stmt_ptr]
@init {
	PDTExpBool * exp_ptr = NULL;
	string name;
}
@after {
}
	: '@pre' (i=IDENTIFIER) {
        string _name ((const char*)$i.text->chars);
        name = _name;
	} '{' 
		(c = conditional_expression ';' {
			if ( exp_ptr == NULL ) { // first time entry
				exp_ptr = (PDTExpBool*)$c.exp_ptr;
			} else {
				PDTAnd * tAnd = new PDTAnd( *cg, *(exp_ptr), *((PDTExpBool*)$c.exp_ptr) );
				exp_ptr = tAnd;
			}
		})+ 
	'}' {
		PDTPreCondition * pre_ptr = new PDTPreCondition( *cg, *exp_ptr, name );
		$stmt_ptr = pre_ptr;
	}
	;
	
/*
 post condition statement 
*/
post_condition returns [PDTStatement * stmt_ptr]
@init {
	PDTExpBool * exp_ptr = NULL;
	string name;
}
@after {
}
	: '@post' (i=IDENTIFIER) {
        string _name ((const char*)$i.text->chars);
        name = _name;
	} '{' 
		(c = conditional_expression ';' {
			if ( exp_ptr == NULL ) { // first time entry
				exp_ptr = (PDTExpBool*)$c.exp_ptr;
			} else {
				PDTAnd * tAnd = new PDTAnd( *cg, *(exp_ptr), *((PDTExpBool*)$c.exp_ptr) );
				exp_ptr = tAnd;
			}
		})+ 
	'}' {
		PDTPostCondition * post_ptr = new PDTPostCondition( *cg, *exp_ptr, name );
		$stmt_ptr = post_ptr;
	}
	;

/*
 assertion statement to handle properties
 */
assertion_statement returns [PDTStatement * stmt_ptr] 
	: assertion_name '(' c=conditional_expression ')' ';' {
		if ( $c.exp_ptr->isBool() ) {
			PDTAssert * assert_ptr = new PDTAssert( *cg, *((PDTExpBool*)$c.exp_ptr) );
			$stmt_ptr = assert_ptr; 
		} else {
			fprintf( stderr, "Assertion needs an boolean expression!\n" );
			exit( EXIT_FAILURE );
		}
	}
	;

/*	
 assertion_name: text for assertions
 */
assertion_name
	: 'assert'
	| 'guarantee'
	;
	
/* 
 compound list of statements (has its own scope)
 */
compound_statement returns [PDTStatement * stmt_ptr]
    : '{' (a=declaration ';' 
    )* (b=statement_list {
            $stmt_ptr = $b.stmt_ptr;
    }
        )? '}'
    ;

/*
 statement_list it's a list of statement separated by ;
 */
statement_list returns [PDTStatement * stmt_ptr]
@init{
    // create a vector of statements to update and then 
    // finally create a PDTListOfStatements to add and return
    int count = 0; // counter of statements
    vector < PDTStatement * > stmtVec; // vector to hold statements
    vector < PDTStatement * >::iterator it; // iterator on the vector
    it = stmtVec.end(); // initialize statement to beginning
}
@after {
    if ( count > 0 ) {
        PDTListOfStatements * list_ptr = new PDTListOfStatements( *cg, stmtVec );
        $stmt_ptr = list_ptr; // return statement list
    }
}
: (a=statement {
		if ( $a.stmt_ptr == NULL ){
        // FADI: this actually happened when we had an empty ';' statement!
        //       maybe we should just ignore it
			cout << "Null statment encountered" << endl;
            } else {
            stmtVec.push_back( $a.stmt_ptr ); // update the vector
            count++; // increase count
            it++; // increment iterator
            }
        })+ 
    ;

/*
 an expression statement
 */
expression_statement returns [PDTStatement * stmt_ptr]
    : ';'
    | a= expression ';' {
        $stmt_ptr = $a.stmt_ptr;
    }
    | 'return' c=conditional_expression ';' {
    	PDTAssign * assign_ptr = new PDTAssign( *cg, *__funcReturn, *$c.exp_ptr, true);
    	$stmt_ptr = assign_ptr;
    }
    ;

/*
 if statement
 */
selection_statement returns [PDTStatement * stmt_ptr]
@init {
    // Create pointers to hold the values of the statements and the
    // conditions and then at the end add them to an ifelse structure
    PDTExpBool * cond_ptr;
    PDTStatement * if_ptr;
    PDTStatement * else_ptr;
    bool isElse = false;
}
@after {
}
    : 'if' '(' a=expression {
            cond_ptr = (PDTExpBool*)$a.exp_ptr;
        } ')' (b=statement {
            if_ptr = $b.stmt_ptr;
        } | '{' s=statement_list {
            if_ptr = $s.stmt_ptr;
        }'}') 
    ('else' (c=statement {else_ptr = $c.stmt_ptr; isElse = true;}| el = compound_statement {else_ptr = $el.stmt_ptr; isElse = true;}))? {
    	if (isElse) { 
        	PDTIfElse * ifelse_ptr = new PDTIfElse( *cg, *cond_ptr, *if_ptr, *else_ptr );
        	$stmt_ptr = ifelse_ptr;
        } else {
        	PDTIfElse * ifelse_ptr = new PDTIfElse( *cg, *cond_ptr, *if_ptr, PDTNilStatement::nil );
        	$stmt_ptr = ifelse_ptr;
        }
    }
    ;

/* 
 while statement
 */
iteration_statement returns [PDTStatement * stmt_ptr]
@init {
    PDTExpBool * cond_ptr;
    PDTStatement * body_ptr;
}
    : 'while' '(' a=expression {
        cond_ptr = (PDTExpBool*)$a.exp_ptr;
    }')' (b=statement {
        body_ptr = $b.stmt_ptr;
    }| c=compound_statement {
        body_ptr = $c.stmt_ptr;
    }) {
        PDTWhile * while_ptr = new PDTWhile( *cg, *cond_ptr, *body_ptr ); 
        $stmt_ptr = while_ptr;
    }
    ;


/*
 function declarations : return type + name + (args) + pre + body + post
 function_declaration
 : type_name IDENTIFIER ( argument_list ) (pre (condtional expression))? { code } (post (conditional_expression))?
 ;
 */
function_declaration
@init {
    string func_name; // the name for function for renaming puproses
    PDTType * ftype; // the return type of the func
    bool ispre = false, ispost=false; // boolean variables to indicate if pre - post are present
    PDTExpBoolBinBool * pre_ptr, * post_ptr; // holders for pre and post conditions
    PDTStatement * stmt_ptr = NULL; // hold statement list
    vector < PDTVar * > argsVec; // argument vector
    vector < PDTVar * > varVec; // variables vector
    PDTVar * retvar_ptr; // return variable
    CGFuncReturn_t ret_type; // return type
}
@after {
	scopeStack.pop_front();
	function_prefix = ""; // reset the function prefix to empty
}
    : t=type_name {
        ret_type = (CGFuncReturn_t)$t.func_type;
    } 
    i=IDENTIFIER {
        string name ((const char*)$i.text->chars);
        func_name = name;
        
        function_prefix = func_name;
        scopeStack.push_front(function_prefix);
        
        switch( ret_type ) {
            case FuncInt: { // integer return type
                ftype = &PDTType::Int;
                string rName = func_name + "::rv";
                PDTNumVar * int_var = new PDTNumVar( *cg, rName );
                retvar_ptr = int_var;
                name2node[rName] = int_var;
                break;
            }
            case FuncBool: { // boolean return type 
                ftype = &PDTType::Bool;
                string rName = func_name + "::rv";
                PDTBoolVar * bool_var = new PDTBoolVar( *cg, rName );
                retvar_ptr = bool_var;
                name2node[rName] = bool_var;
                break;
            } 
            case FuncVoid: { // void return 
                ftype = &PDTType::Void;
                string rName = func_name + "::rv";
                PDTNumVar * int_var = new PDTNumVar( *cg, rName );
                retvar_ptr = int_var;
                name2node[rName] = int_var;
                break;
            } 
            default:
                break;
        }
        __funcReturn = retvar_ptr; // update the global function return variable for the return statment
    }
    '(' (d=declaration {
        argsVec.push_back($d.var_ptr);
    }
    (',' d1=declaration {
        argsVec.push_back($d1.var_ptr); 
    })*)?
    ')' ('pre' '(' pre_c=conditional_expression {
        ispre = true;
        pre_ptr = dynamic_cast<PDTExpBoolBinBool*>( $pre_c.exp_ptr );
    }')' )?
    '{' (a=declaration ';' {
    	if ($a.var_ptr == NULL) {
    		cerr << "Variable Pointer NULL" << endl;
    		exit(EXIT_FAILURE);
    	}
    	varVec.push_back($a.var_ptr);
    })* (s=statement_list {
        stmt_ptr = $s.stmt_ptr;
    })?  '}'
    ('post' '(' post_c=conditional_expression ')' {
        ispost = true;
        post_ptr = dynamic_cast<PDTExpBoolBinBool*>( $post_c.exp_ptr );
    })? {
        if (stmt_ptr == NULL) {
            cerr << "Function " << func_name << " has no body." << endl;
    		exit(EXIT_FAILURE);

        }
        // create function definition structure with all the above 
        // information about the declaration 
        PDTFunctionDec * func_ptr = new PDTFunctionDec( *cg, func_name, *(dynamic_cast<PDTListOfStatements*>(stmt_ptr)), retvar_ptr, argsVec );
        name2func[func_name] = func_ptr;
        
        // go over args vec and var vec and assign function declaration for each variable
        for (unsigned int i = 0; i < argsVec.size(); i++) {
        	argsVec[i]->setFunctionDec(func_ptr);
        }
        for (unsigned int i = 0; i < varVec.size(); i++) {
        	varVec[i]->setFunctionDec(func_ptr);
        }
        
        // go over list of unresolved calls and check if any needs to be updated
        UnresolvedFunc_t::iterator _it = unresolvedfunc.begin();
        for(; _it != unresolvedfunc.end();_it++) {
            if ( (*_it)->getName() == func_name ) { // can resolve now
                (*_it)->setFunctionDec( func_ptr, *cg );
                string msg;
                if ( !(*_it)->checkConsistency( msg ) ) {
                	fprintf( stderr, "Consistency check: \%s \n", msg.c_str() );
                	exit( EXIT_FAILURE );
                }
                unresolvedSize--;
                //TODO: why not remove it from the vector as well?
            }
        }
        
        // go over list of unresolved assignments and resolve them
        Name2Assign_t::iterator assign_it;
        pair < Name2Assign_t::iterator, Name2Assign_t::iterator > ret;
        ret = name2assign.equal_range( func_name );
        for ( assign_it = ret.first; assign_it != ret.second; assign_it++ ) {
            // TODO: check what is happening here!?
            PDTAssign * assign = (*assign_it).second;
        	//(*assign_it).second->replaceExpression( *(dynamic_cast<PDTExp*> ( func_ptr->getReturnVar()->getNode() )) );
        	assign->replaceExpression( *(dynamic_cast<PDTExp*> ( func_ptr) ));
        }
    }
    ;



/*
 function return type 
 */
type_name returns [int func_type] 
    : 'int' {$func_type = (int)FuncInt;}
    | 'boolean'{$func_type = (int)FuncBool;}
    | 'void' {$func_type = (int)FuncVoid;}
    ;

/*
 code for functions
 */
func_code
    : a=declaration ';'
    | statement_list
    ;
/* 
function_call_statement returns [PDTStatement * stmt_ptr]
	: f=function_call {
		$stmt_ptr = $f.stmt_ptr;
	} ';'
	;
*/
/*
 function call
function_call returns [PDTStatement * stmt_ptr]
@init {
	bool isArg = false;
}
    : i=IDENTIFIER '(' (a=arg_list {isArg = true;})? ')' {
        // find the functions name and check if it is declared
        // if it is then point add it to the constructor of the 
        // function call, else leave for later to check 
        string name ((const char*)$i.text->chars);
        
        PDTFunctionCall * funcCall_ptr;
		if (isArg) {
        	funcCall_ptr = new PDTFunctionCall( *cg, *($a.argVec), name, NULL );
        } else {
        	funcCall_ptr = new PDTFunctionCall( *cg, name, NULL );
        }

        Name2Func_t::iterator it;
        it = name2func.find(name);
        if ( it == name2func.end() ) { // not found
            unresolvedfunc.push_back( funcCall_ptr );
            unresolvedSize++;
        } else { // found
            funcCall_ptr->setFunctionDec( (*it).second, *cg );
        }

        $stmt_ptr = funcCall_ptr;
    } 
    ;
*/

argument_list returns [vector < PDTNode *> * argVec]
@init{
    vector < PDTNode * > * _argVec = new vector < PDTNode* > ();
}
  : (a1=argument {
            _argVec->push_back( $a1.arg);
          } 
     (',' a2=argument {
            _argVec->push_back( $a2.arg);
      })*
    ) 
  {
        $argVec = _argVec;
  }
  ;

argument returns [PDTNode * arg]
  : i=IDENTIFIER {
        string name ((const char*)$i.text->chars);
        bool found = false;
		PDTNode * varNode = checkName(name, scopeStack, name2node, found);
        if ( !found ) { // argument variable not found
            fprintf( stderr, "Argument \%s not found on line \%d\n", name.c_str(), $i.line);
            exit( EXIT_FAILURE ); 
        }
        $arg = varNode;
  }
  | exp=conditional_expression {
      $arg=$exp.exp_ptr;
  }
  ;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// end parser rules

// L E X E R  R U L E S
STRING: '"' .* '"' ;

IDENTIFIER
    :	LETTER (LETTER|'0'..'9')*
    ;
	
fragment
LETTER
    :	'$'
    |	'A'..'Z'
    |	'a'..'z'
    |	'_'
    ;

DECIMAL_LITERAL : ('0' | '1'..'9' '0'..'9'*);

WS  :  (' '|'\r'|'\t'|'\u000C'|'\n') {$channel=HIDDEN;}
    ;

LINE_COMMENT
    : '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
    ;
/////////////////////////////////////////////////////////
// end MINI.g
